﻿using System;
using System.Collections.Generic;
using SDT.CodeSystem.Data;
using SDT.Libraries.Mapper;

namespace SDT.CodeSystem
{
    /// <summary>
    /// Written & reviewed by SinhNT
    /// </summary>
    public sealed class City : ICity
    {
        #region Static lists
        // The list that contains all cities
        public static List<ICity> TheList = null;
        // The list that contains all northen cities
        public static List<ICity> TheNorthenList = null;
        // The list that contains all southern cities
        public static List<ICity> TheSouthernList = null;
        // The list that contains all central cities
        public static List<ICity> TheCentralList = null;
        #endregion

        #region Static methods
        /// <summary>
        /// Get a city by city's code from TheList
        /// </summary>
        /// <param name="code">City's Code</param>
        /// <returns>Found city object or null</returns>
        public static ICity GetByCode(CityCodes.Codes code)
        {
            foreach (var i in TheList)
                if (i.Code == code)
                    return i;
            return null;
        }

        /// <summary>
        /// Retrieve a mapping list between city's code and city's name.
        /// </summary>
        /// <param name="AreaCode">MB, MT, MN, CN</param>
        /// <returns>Mapping list between city's code and city's name</returns>
        public static IList<StringStringMapItem> ToList(CityCodes.Codes AreaCode)
        {
            List<ICity> list = null;
            switch (AreaCode)
            {
                case CityCodes.Codes.CN:
                    list = TheList;
                    break;
                case CityCodes.Codes.MB:
                    list = TheNorthenList;
                    break;
                case CityCodes.Codes.MT:
                    list = TheCentralList;
                    break;
                case CityCodes.Codes.MN:
                    list = TheSouthernList;
                    break;
                default:
                    throw new InvalidOperationException();
            }
            var l = new List<StringStringMapItem>();
            foreach (var i in list)
                l.Add(new StringStringMapItem(CityCodes.ToString(i.Code), i.Name));
            return l;
        }

        public static IList<StringStringMapItem> ToList()
        {
            var l = new List<StringStringMapItem>();
            foreach (var i in TheList)
                if(i.Code != CityCodes.Codes.CN && i.Code != CityCodes.Codes.MB && i.Code != CityCodes.Codes.MT && i.Code != CityCodes.Codes.MN)
                    l.Add(new StringStringMapItem(CityCodes.ToString(i.Code), i.Name));
            return l;
        }

        private static void LoadLists()
        {
            #region Load all cities
            if (TheList != null)
                TheList.Clear();
            TheList = Repository.LoadCities(); 
            #endregion

            #region Initialize other lists
            if (TheNorthenList != null)
                TheNorthenList.Clear();
            else
                TheNorthenList = new List<ICity>();

            if (TheSouthernList != null)
                TheSouthernList.Clear();
            else
                TheSouthernList = new List<ICity>();

            if (TheCentralList != null)
                TheCentralList.Clear();
            else
                TheCentralList = new List<ICity>(); 
            #endregion

            #region Add to other lists
            foreach (var C in TheList)
            {
                List<ICity> l = null;
                switch (C.AreaCode)
                {
                    case CityCodes.Codes.MB:
                        l = TheNorthenList;
                        break;
                    case CityCodes.Codes.MT:
                        l = TheCentralList;
                        break;
                    case CityCodes.Codes.MN:
                        l = TheSouthernList;
                        break;
                    default:
                        break;
                }
                if (l != null)
                    l.Add(C);
            } 
            #endregion
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Load cities to the static lists (TheList, TheNorthenList, TheSouthernList, TheCentralList)
        /// </summary>
        static City()
        {
            LoadLists();
        }

        /// <summary>
        /// Raw constructor
        /// </summary>
        internal City(CityCodes.Codes area, CityCodes.Codes city, int prio, string Name, int nItems)
        {
            this.code = city;
            this.priority = prio;
            this.areaCode = area;
            this.name = Name;
            this.nitems = nItems;
        }
        #endregion

        #region ICity Members
        private CityCodes.Codes code = CityCodes.Codes.CN;
        public CityCodes.Codes Code
        {
            get { return code; }
        }

        public string CodeAsString
        {
            get { return CityCodes.StrCodes[(int)code]; }
        }

        private int priority = 100;
        public int Priority
        {
            get { return priority; }
        }

        private CityCodes.Codes areaCode = CityCodes.Codes.CN;
        public CityCodes.Codes AreaCode
        {
            get { return areaCode; }
        }

        private string name = string.Empty;
        public string Name
        {
            get { return name; }
        }
        
        private int nitems = 0;
        public int nItems
        {
            get { return nitems; }
        }

        // Lazy-loading
        private IList<IDistrict> districts = null;
        public IList<IDistrict> Districts
        {
            get 
            {
                if (districts != null)
                    return districts;

                // Load district
                districts = Repository.LoadDistricts(this);

                return districts;
            }
        }

        // Indexer
        public IDistrict this[int id]
        {
            get
            {
                if(Districts != null)
                    foreach (var d in Districts)
                        if (d.Id == id)
                            return d;
                return null;
            }
        }

        // Indexer
        public IDistrict this[string ShortName]
        {
            get
            {
                if (Districts != null)
                    foreach (var d in Districts)
                        if (d.ShortName == ShortName)
                            return d;
                return null;
            }
        }

        public IList<IntStringMapItem> ToDistrictMapList()
        {
            var l = new List<IntStringMapItem>();
            foreach (var d in Districts)
                l.Add(new IntStringMapItem(d.Id, d.Name));
            return l;
        }
        #endregion
    }
}